using System;
using System.Runtime.InteropServices;
using Reinterpret.Net;
using System.Buffers;
using StbImageResizeSharp;
namespace Gltf
{
    public struct PixelRectangle
    {
        /**
         * @brief The X coordinate of the top-left corner of the rectangle.
         */
        public int x;

        /**
         * @brief The Y coordinate of the top-left corner of the rectangle.
         */
        public int y;

        /**
         * @brief The total number of pixels in the horizontal direction.
         */
        public int width;

        /**
         * @brief The total number of pixels in the vertical direction.
         */
        public int height;

        public PixelRectangle(int x, int y, int width, int height)
        {
            this.x = x;
            this.y = y;
            this.width = width;
            this.height = height;
        }
    }
    public class ImageManipulation
    {

        /**
         * @brief Directly copies pixels from a source to a target, without validating
         * the provided pointers or ranges.
         *
         * @param pTarget The pointer at which to start writing pixels.
         * @param targetRowStride The number of bytes between rows in the target
         * image.
         * @param pSource The pointer at which to start reading pixels.
         * @param sourceRowStride The number of bytes between rows in the source
         * image.
         * @param sourceWidth The number of pixels to copy in the horizontal
         * direction.
         * @param sourceHeight The number of pixels to copy in the vertical direction.
         * @param bytesPerPixel The number of bytes used to represent each pixel.
         */
        public static unsafe void unsafeBlitImage(byte* pTarget, int targetRowStride, byte* pSource, int sourceRowStride, int sourceWidth, int sourceHeight, int bytesPerPixel)
        {
            int bytesToCopyPerRow = bytesPerPixel * sourceWidth;

            if (bytesToCopyPerRow == targetRowStride &&
                targetRowStride == sourceRowStride)
            {
                // Copy a single contiguous block with all data.
                System.Buffer.MemoryCopy(pSource,pTarget, sourceWidth * sourceHeight * bytesPerPixel, sourceWidth * sourceHeight * bytesPerPixel);
            }
            else
            {
                // Copy row by row
                for (int j = 0; j < sourceHeight; ++j)
                {
                    System.Buffer.MemoryCopy(pTarget, pSource, bytesToCopyPerRow, bytesToCopyPerRow);
                    pTarget += targetRowStride;
                    pSource += sourceRowStride;
                }
            }
        }

        /**
         * @brief Copies pixels from a source image to a target image.
         *
         * If the source and target dimensions are the same, the source pixels are
         * copied exactly into the target. If not, the source image is scaled to fit
         * the target rectangle.
         *
         * The filtering algorithm for scaling is not specified, but can be assumed
         * to provide reasonably good quality.
         *
         * The source and target images must have the same number of channels and same
         * bytes per channel. If scaling is required, they must also use exactly 1
         * byte per channel. If any of these requirements are violated, this function
         * will return false and will not change any target pixels.
         *
         * The provided rectangles are validated to ensure that they fall within the
         * range of the images. If they do not, this function will return false and
         * will not change any pixels.
         *
         * @param target The image in which to write pixels.
         * @param targetPixels The pixels to write in the target.
         * @param source The image from which to read pixels.
         * @param sourcePixels The pixels to read from the target.
         * @returns True if the source image was blitted successfully into the target,
         * or false if the blit could not be completed due to invalid ranges or
         * incompatible formats.
         */
        public static unsafe bool blitImage(ImageCesium target, PixelRectangle targetPixels, ImageCesium source, PixelRectangle sourcePixels)
        {
            if (sourcePixels.x < 0 || sourcePixels.y < 0 || sourcePixels.width < 0 || sourcePixels.height < 0 ||
    (sourcePixels.x + sourcePixels.width) > source.width ||
    (sourcePixels.y + sourcePixels.height) > source.height)
            {
                // Attempting to blit from outside the bounds of the source image.
                return false;
            }

            if (targetPixels.x < 0 || targetPixels.y < 0 ||
                (targetPixels.x + targetPixels.width) > target.width ||
                (targetPixels.y + targetPixels.height) > target.height)
            {
                // Attempting to blit outside the bounds of the target image.
                return false;
            }

            if (target.channels != source.channels ||
                target.bytesPerChannel != source.bytesPerChannel)
            {
                // Source and target image formats don't match; currently not supported.
                return false;
            }

            int bytesPerPixel = (int)(target.bytesPerChannel * target.channels);
            int bytesPerSourceRow = bytesPerPixel * (int)(source.width);
            int bytesPerTargetRow = bytesPerPixel * (int)(target.width);

            int requiredTargetSize = (int)(targetPixels.height) * bytesPerTargetRow;
            int requiredSourceSize = (int)(sourcePixels.height) * bytesPerSourceRow;
            if (target.pixelData.Length < requiredTargetSize ||
                source.pixelData.Length < requiredSourceSize)
            {
                return false;
            }

            // Position both pointers at the start of the first row.

            fixed (byte* fpTarget = target.pixelData, fpSource = source.pixelData)
            {
                byte* pTarget = fpTarget;
                byte* pSource = fpSource;
                pTarget += (int)(targetPixels.y) * bytesPerTargetRow + (int)(targetPixels.x) * bytesPerPixel;
                pSource += (int)(sourcePixels.y) * bytesPerSourceRow + (int)(sourcePixels.x) * bytesPerPixel;
                if (sourcePixels.width == targetPixels.width &&
               sourcePixels.height == targetPixels.height)
                {
                    // Simple, unscaled, byte-for-byte image copy.
                    unsafeBlitImage(
                        pTarget,
                        bytesPerTargetRow,
                        pSource,
                        bytesPerSourceRow,
                        (int)(sourcePixels.width),
                        (int)(sourcePixels.height),
                        bytesPerPixel);
                }
                else
                {
                    if (target.bytesPerChannel != 1)
                    {
                        // We currently only support resizing images that use 1 byte per channel.
                        return false;
                    }

                    // Use STB to do the copy / scale
                    StbImageResize.stbir_resize_uint8(pTarget, sourcePixels.width, sourcePixels.height,
                    (int)(bytesPerSourceRow), pSource, targetPixels.width, targetPixels.height, (int)(bytesPerTargetRow), target.channels);
                }
            }
            return true;
        }
    }
}